package com.rongxun.utils.wallet;

import com.rongxun.hbwallet.constants.HttpConstants;
import com.rongxun.utils.FlyLog;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.Web3jFactory;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.admin.AdminFactory;
import org.web3j.protocol.admin.methods.response.PersonalUnlockAccount;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthEstimateGas;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * 创建者     庄丰泽
 * 创建时间   2018/8/17 17:19
 * 描述	      交易相关
 */

public class TransactionUtils {

    private static String tokenAddress = HttpConstants.Token.TOKEN_ADDRESS;//代币地址
    private static String tokenUrl     = HttpConstants.Token.TOKEN_URL;//节点地址

    public static BigInteger getCityBalance(String fromAddress) {

        String methodName = "balanceOf";

        Admin web3j = AdminFactory.build(new HttpService(tokenUrl));

        List<Type> inputParameters = new ArrayList<>();
        List<TypeReference<?>> outputParameters = new ArrayList<>();
        Address addresss = new Address(fromAddress);
        inputParameters.add(addresss);
        TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
        };
        outputParameters.add(typeReference);

        Function function = new Function(methodName, inputParameters, outputParameters);
        String data = FunctionEncoder.encode(function);

        Transaction transaction = Transaction.createEthCallTransaction(fromAddress, tokenAddress, data);
        EthCall ethCall;
        BigInteger balanceValue = BigInteger.ZERO;
        try {
            ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send();
            List<Type> results = FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters());


            balanceValue = (BigInteger) results.get(0).getValue();

            FlyLog.i("CityBalance" + (BigInteger) results.get(0).getValue());

        } catch (IOException e) {
            e.printStackTrace();
        }

        return balanceValue;
    }

    public static BigInteger getEthBalance(String fromAddress) {
        Web3j web3j = Web3jFactory.build(new HttpService(tokenUrl));
        BigInteger balance = null;
        try {
            balance = web3j.ethGetBalance(fromAddress, DefaultBlockParameterName.PENDING).send().getBalance();
        } catch (IOException e) {
            e.printStackTrace();
        }
        FlyLog.i("ethBalance " + balance);
        return balance;

    }


    /**
     * 代币转账
     */
    public static String sendTokenTransaction(String fromAddress, String password, String toAddress, BigInteger amount, BigInteger gasLimit, BigInteger gasPrice) {
        String txHash = null;
        Admin admin = AdminFactory.build(new HttpService(tokenUrl));
        // TODO: 2018/8/21 把amount转成最小单位
        try {
            PersonalUnlockAccount personalUnlockAccount = admin.personalUnlockAccount(
                    fromAddress, password, BigInteger.valueOf(10)).send();
            if (personalUnlockAccount.accountUnlocked()) {

                String methodName = "transfer";
                List<Type> inputParameters = new ArrayList<>();
                List<TypeReference<?>> outputParameters = new ArrayList<>();

                Address tAddress = new Address(toAddress);

                Uint256 value = new Uint256(amount);

                inputParameters.add(tAddress);
                inputParameters.add(value);

                TypeReference<Bool> typeReference = new TypeReference<Bool>() {
                };
                outputParameters.add(typeReference);

                Function function = new Function(methodName, inputParameters, outputParameters);

                String data = FunctionEncoder.encode(function);

                EthGetTransactionCount ethGetTransactionCount = admin
                        .ethGetTransactionCount(fromAddress, DefaultBlockParameterName.PENDING).sendAsync().get();

                BigInteger nonce = ethGetTransactionCount.getTransactionCount();


                //                BigInteger gasPrice = Convert.toWei(GasPrice + "", Convert.Unit.ETHER).toBigInteger();


                Transaction transaction = Transaction.createFunctionCallTransaction(fromAddress, nonce, gasPrice,
                        gasLimit, tokenAddress, data);

                EthSendTransaction ethSendTransaction = admin.ethSendTransaction(transaction).sendAsync().get();
                txHash = ethSendTransaction.getTransactionHash();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return txHash;
    }

    /**
     * 生成一个普通交易对象
     *
     * @param fromAddress 放款方
     * @param toAddress   收款方
     * @param nonce       交易序号
     * @param gasPrice    gas 价格
     * @param gasLimit    gas 数量
     * @param value       金额
     * @return 交易对象
     */
    public static Transaction makeTransaction(String fromAddress, String toAddress,
                                              BigInteger nonce, BigInteger gasPrice,
                                              BigInteger gasLimit, BigInteger value) {
        Transaction transaction = Transaction.createEtherTransaction(fromAddress, nonce, gasPrice, gasLimit, toAddress, value);
        return transaction;
    }

    /**
     * 获取普通交易的gas上限
     *
     * @param transaction 交易对象
     * @return gas 上限
     */
    public static BigInteger getTransactionGasLimit(Transaction transaction) {
        BigInteger gasLimit = new BigInteger("21000");

        Admin admin = AdminFactory.build(new HttpService(tokenUrl));

        try {
            EthEstimateGas ethEstimateGas = admin.ethEstimateGas(transaction).send();
            gasLimit = ethEstimateGas.getAmountUsed();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return gasLimit;
    }

    public static BigInteger getTransactionGasPrice() {
        BigInteger gasPrice = new BigInteger("11000");

        Admin admin = AdminFactory.build(new HttpService(tokenUrl));
        try {
            EthGasPrice send = admin.ethGasPrice().send();
            gasPrice = send.getGasPrice();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return gasPrice;
    }

    /**
     * 获取账号交易次数 nonce
     *
     * @param fromAddress 钱包地址
     * @return nonce
     */
    public static BigInteger getTransactionNonce(String fromAddress) {
        BigInteger nonce = BigInteger.ZERO;
        Admin admin = AdminFactory.build(new HttpService(tokenUrl));
        try {
            EthGetTransactionCount ethGetTransactionCount = admin.ethGetTransactionCount(fromAddress, DefaultBlockParameterName.PENDING).send();
            nonce = ethGetTransactionCount.getTransactionCount();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return nonce;
    }

    /**
     * 发送一个普通交易
     *
     * @return 交易 Hash
     */
    public static String sendTransaction(String fromAddress, String password, String toAddress, BigInteger gasPrice, BigInteger gasLimit, BigDecimal amount) {
        Admin admin = AdminFactory.build(new HttpService(tokenUrl));
        BigInteger unlockDuration = BigInteger.valueOf(60L);
        String txHash = null;
        try {
            PersonalUnlockAccount personalUnlockAccount = admin.personalUnlockAccount(fromAddress, password, unlockDuration).send();
            if (personalUnlockAccount.accountUnlocked()) {
                BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

                //不是必须的 缺省值就是正确的值
                BigInteger nonce = getTransactionNonce(fromAddress);

                //该值为大部分矿工可接受的gasPrice
                //                BigInteger gasPrice = new BigInteger(GasPrice);

                Transaction transaction = makeTransaction(fromAddress, toAddress,
                        nonce, gasPrice,
                        gasLimit, value);
                EthSendTransaction ethSendTransaction = admin.ethSendTransaction(transaction).send();
                txHash = ethSendTransaction.getTransactionHash();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("tx hash " + txHash);
        return txHash;
    }

}
